package com.tuocent.dagv2.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tuocent.dagv2.common.entity.InputNums;
import com.tuocent.dagv2.common.entity.LoginUserInfo;
import com.tuocent.dagv2.common.utils.PublicUtil;
import com.tuocent.dagv2.common.utils.SnowFlakeID;
import com.tuocent.dagv2.constant.PublicConst;
import com.tuocent.dagv2.entity.*;
import com.tuocent.dagv2.mapper.ArchivesDigitalFileMapper;
import com.tuocent.dagv2.mapper.ArchivesImportMapper;
import com.tuocent.dagv2.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.imaging.ImageInfo;
import org.apache.commons.imaging.Imaging;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Vincent
 * @since 2025-09-17
 */
@Service
@Slf4j
public class ArchivesDigitalFileServiceImpl extends ServiceImpl<ArchivesDigitalFileMapper, ArchivesDigitalFile> implements IArchivesDigitalFileService {
    @Value("${location.archives-dir}")
    private String archivesDir;

    @Resource
    private SnowFlakeID snowFlakeID;
    @Autowired
    private IProjectsService projectsService;
    @Autowired
    private ITaskService taskService;
    @Autowired
    private ArchivesImportMapper archivesImportMapper;
    @Autowired
    private IArchivesRecordService archivesRecordService;
    @Autowired
    private IArchivesFilesService archivesFilesService;
    @Autowired
    private IArchivesImportLogService archivesImportLogService;

    @Override
    public void imagesImportTaskExecute(LoginUserInfo loginUser, ArchivesImport data, File srcDir, String onlyField, String fillingModel) {
        long startTime = System.currentTimeMillis();
        Object archives = null;
        int idx = 0;
        InputNums numInfo = new InputNums();
        String pid = data.getPid();
        String tid = data.getTid();
        String archivesId = "";
        String batchCode = "";
        Projects project = projectsService.getById(pid);
        Task task = taskService.getById(tid);
        if (project == null || task == null) {
            data.setStatus(3);
            data.setInfo("无效的项目或任务");
            archivesImportMapper.updateById(data);
            return;
        }
        String projectName = project.getName();
        String taskName = task.getTaskName();
        for (File file : Objects.requireNonNull(srcDir.listFiles())) {
            //查目录列表，把对应的数据导入相应的数据
            if (file.isDirectory()) {
                //增加加一个目录数量
                numInfo.setDirNum(numInfo.getDirNum() + 1);
                String dirName = file.getName();
                switch (fillingModel) {
                    //按件归档
                    case "1":
                        archives = archivesRecordService.getOne(
                                new QueryWrapper<ArchivesRecord>()
                                        .eq("pid", pid)
                                        .eq("tid", data.getTid())
                                        .eq(onlyField, dirName)
                                        .eq("is_del", "0"));
                        if (archives == null) {
                            archivesImportLogService.ImportLogData(data.getId(), idx, "没有找到 " + dirName + " 对应的数据");
                            numInfo.setDirErrNum(numInfo.getDirErrNum() + 1);
                            continue;
                        }
                        archivesId = ((ArchivesRecord) archives).getId();
                        batchCode = ((ArchivesRecord) archives).getBatchCode();
                        break;
                    //按卷归档
                    case "2":
                        archives = archivesFilesService.getOne(
                                new QueryWrapper<ArchivesFiles>()
                                        .eq("pid", pid)
                                        .eq("tid", data.getTid())
                                        .eq(onlyField, dirName)
                                        .eq("is_del", "0"));
                        if (archives == null) {
                            archivesImportLogService.ImportLogData(data.getId(), idx, "没有找到 " + dirName + " 对应的数据");
                            numInfo.setDirErrNum(numInfo.getDirErrNum() + 1);
                            continue;
                        }
                        archivesId = ((ArchivesFiles) archives).getId();
                        batchCode = ((ArchivesFiles) archives).getBatchCode();
                        break;
                }

                //图像目录格式
                //目标目录 根目录+项目ID+任务ID+图像类别+批次号+档号
                //String dtsDir = projectName + "/" +
                //        taskName + "/" +
                //        data.getModel() + "/" +
                //        batchCode + "/" +
                //        dirName + "/";
                String separator = File.separator;
                //存储目录格式 基本目录 项目ID 任务ID 图像类别 档案ID（之所以使用ID号，是因为减少因为改名而找不到文件）
                String dtsDir = archivesDir + separator + pid + separator + tid + separator + data.getModel() + separator + batchCode + separator + archivesId + separator;

                //File dir = new File(archivesDir + "/" + dtsDir);
                File dir = new File(dtsDir);
                //创建存储目录
                if (!dir.exists() && !dir.isDirectory()) {
                    try {
                        if (!dir.mkdirs()) {
                            archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + " 导入失败，错误：文无法创建文件存储目录");
                            numInfo.setDirErrNum(numInfo.getDirErrNum() + 1);
                            continue;
                        }
                    } catch (Exception E) {
                        archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + " 导入失败，错误：文无法创建文件存储目录");
                        numInfo.setDirErrNum(numInfo.getDirErrNum() + 1);
                        continue;
                    }
                }

                File[] files = file.listFiles();
                if (files != null) {
                    Arrays.sort(files, new AlphanumericComparator());
                }

                assert files != null;
                for (File srcFile : files) {
                    String fileName = srcFile.getName();
                    String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
                    if (Arrays.asList(PublicConst.DEF_MOUNT_FILE).contains(extension)) {
                        numInfo.setSumNum(numInfo.getSumNum() + 1);

                        String srcF = file + "/" + fileName;
                        String newFile = dtsDir + fileName;
                        try {
                            //取文件MD5
                            FileInputStream fileInputStream = new FileInputStream(srcFile);
                            long size = fileInputStream.available();
                            byte[] bf;
                            if (size > PublicConst.MD5CheckSize) {
                                bf = new byte[PublicConst.MD5CheckSize];
                            } else {
                                bf = new byte[(int) size];
                            }
                            fileInputStream.read(bf);
                            String md5 = DigestUtils.md5DigestAsHex(bf);
                            fileInputStream.close();

                            //文件是否存在
                            ArchivesDigitalFile digiFile = getOne(
                                    new QueryWrapper<ArchivesDigitalFile>()
                                            .eq("pid", pid)
                                            .eq("tid", tid)
                                            .eq("archivesid", archivesId)
                                            .eq("filemodel", fillingModel)
                                            .eq("filename", fileName)
                                            .last("limit 1"));
                            if (digiFile != null) {
                                digiFile.setUpdateTime(LocalDateTime.now());
                                digiFile.setUserId(loginUser.getUserCode());
                            } else {
                                //如文件不存在
                                digiFile = new ArchivesDigitalFile();
                                digiFile.setId(snowFlakeID.getId());
                                digiFile.setPid(pid);
                                digiFile.setTid(tid);
                                digiFile.setArchivesId(archivesId);
                                digiFile.setFileIdx(idx);
                                digiFile.setIsDel("0");
                                digiFile.setSrcFileName(fileName);
                                digiFile.setFileModel(data.getModel().toString());
                                digiFile.setBatchCode(batchCode);
                                digiFile.setUploadTime(LocalDateTime.now());
                                digiFile.setUserId(loginUser.getUserCode());
                            }
                            digiFile.setMd5(md5);
                            digiFile.setPatch(dtsDir);
                            //取文件信息
                            digiFile.setFileType(extension);
                            try {
                                ImageInfo imageInfo = Imaging.getImageInfo(srcFile);
                                if (imageInfo == null) {
                                    archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + "-" + fileName + " 导入失败，错误：文件不是一个有效的JPG文件");
                                    numInfo.setErrNum(numInfo.getErrNum() + 1);
                                    continue;
                                }
                                digiFile.setDpi(String.valueOf(imageInfo.getPhysicalWidthDpi()));
                                digiFile.setImgfh(String.valueOf(imageInfo.getHeight()));
                                digiFile.setImgfw(String.valueOf(imageInfo.getWidth()));
                                digiFile.setImgh(String.valueOf(Math.round(imageInfo.getPhysicalHeightInch() * 2.54 * 10)));
                                digiFile.setImgw(String.valueOf(Math.round(imageInfo.getPhysicalWidthInch() * 2.54 * 10)));
                            } catch (Exception E) {
                                archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + "-" + fileName + " 挂接失败，错误：" + E.getMessage());
                                numInfo.setErrNum(numInfo.getErrNum() + 1);
                                continue;
                            }

                            //复制文件
                            try {
                                digiFile.setMd5(md5);
                                digiFile.setFileSize(size);
                                digiFile.setFilename(fileName);
                                saveOrUpdate(digiFile);
                        /*
                        //record.setDigi("1");
                        UpdateWrapper up = new UpdateWrapper<>().set("digi", "1")
                                .eq("id", record.getId())
                                .eq("fonds_id", record.getFondsId())
                                .eq("class_id", record.getClassId());
                        recordService.update(up);
                        */
                                Files.move(Paths.get(srcFile.toString()), Paths.get(newFile), StandardCopyOption.REPLACE_EXISTING);
                                numInfo.setAccNum(numInfo.getAccNum() + 1);
                            } catch (Exception e) {
                                numInfo.setErrNum(numInfo.getErrNum() + 1);
                                archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + "-" + fileName + " 挂接失败，错误：" + e.getMessage());
                            }
                        } catch (Exception e) {
                            numInfo.setErrNum(numInfo.getErrNum() + 1);
                            archivesImportLogService.ImportLogData(data.getId(), idx, "文件 " + dirName + "-" + fileName + " 挂接失败，错误：" + e.getMessage());
                        }
                        idx = idx + 1;
                    }
                }
                if (!file.delete()) {
                    log.error("删除导入原文件失败");
                }
            }
        }
        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;  //用时
        data.setInfo("共导入 " + numInfo.getSumNum() + "个文件：其中成功导入" + numInfo.getAccNum() + "个文件;失败 " +
                numInfo.getErrNum() + "个文件。 用时：" + PublicUtil.getMillisToTime(totalTime));
        data.setStatus(2);
        archivesImportMapper.updateById(data);
    }

    public static class AlphanumericComparator implements Comparator<File> {

        private final boolean isDigit;

        public AlphanumericComparator() {
            this(true);
        }

        public AlphanumericComparator(boolean isDigit) {
            this.isDigit = isDigit;
        }

        private static final String ZERO = "0";

        public int compare(File f1, File f2) {
            if (isDigit) {
                // 数字序列排序
                String s1 = f1.getName();
                String s2 = f2.getName();
                int i1 = extractInt(s1);
                int i2 = extractInt(s2);
                return Integer.compare(i1, i2);
            } else {
                // 字典序排序
                return f1.getName().compareTo(f2.getName());
            }
        }

        private int extractInt(String s) {
            String digits = s.replaceAll("\\D+", "");
            return digits.isEmpty() ? 0 : Integer.parseInt(digits);
        }
    }

}
